/**
  ******************************************************************************
  * @file    AT25DF041.c 
  * @author  Thongnh
  * @version V1.0
  * @date    18-07-2011
  * @brief   Main process of flash AT25DF041
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, XICUTELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 Xicutelectronics</center></h2>
  ******************************************************************************
  */ 
#include "AT25DF041.h"
#if defined (__FW_BUILD__)
extern "C"  {
#include "stm32f10x.h"
}
#endif


void cFlash::CsLow()
{
#if defined (__FW_BUILD__)
    GPIO_ResetBits(sFLASH_CS_GPIO_PORT, sFLASH_CS_PIN);
#endif
}

void cFlash::CsHigh()
{
#if defined (__FW_BUILD__)
    GPIO_SetBits(sFLASH_CS_GPIO_PORT, sFLASH_CS_PIN);
#endif
}
/*******************************************************************************
Function name: USART_AppInit
Description: This function initialize USART1
Input: baud_rate -> Baud rate for communication
Output: None
*******************************************************************************/
void cFlash::InitReset()
{
#if defined (__FW_BUILD__)
    GPIO_InitTypeDef    GPIO_InitStructure;
    SPI_InitTypeDef     SPI_InitStructure;

    /*!< sFLASH_SPI_CS_GPIO, sFLASH_SPI_MOSI_GPIO, sFLASH_SPI_MISO_GPIO 
       and sFLASH_SPI_SCK_GPIO Periph clock enable */
    RCC_APB2PeriphClockCmd(sFLASH_CS_GPIO_CLK | sFLASH_SPI_MOSI_GPIO_CLK | sFLASH_SPI_MISO_GPIO_CLK |
                         sFLASH_SPI_SCK_GPIO_CLK, ENABLE);

    /*!< sFLASH_SPI Periph clock enable */
    if (sFLASH_SPI == SPI1)
    {
        RCC_APB2PeriphClockCmd(sFLASH_SPI_CLK, ENABLE);
    }
    else
    {
        RCC_APB1PeriphClockCmd(sFLASH_SPI_CLK, ENABLE);
    }
    
    /*!< Configure sFLASH_SPI pins: SCK */
    GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_SCK_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_SPI pins: MOSI */
    GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_MOSI_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(sFLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_SPI pins: MISO */
    GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_MISO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
    GPIO_Init(sFLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);

    /*!< Configure sFLASH_CS_PIN pin: sFLASH Card CS pin */
    GPIO_InitStructure.GPIO_Pin = sFLASH_CS_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
    CsHigh();

#if 0
    /*!< SPI configuration */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Cmd(sFLASH_SPI, ENABLE);
#endif

    /*!< SPI configuration */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(sFLASH_SPI, &SPI_InitStructure);

    /*!< Enable the sFLASH_SPI  */
    SPI_Cmd(sFLASH_SPI, ENABLE);
#endif

    CsHigh();
    if (ReadStatus() & 0x80)
    {
        GlobalProtect();
    }
}

/*******************************************************************************
Function name: FlashSendByte
Description: Send and receive one byte on SPI
Input: byte -> data output
Output: return data received
*******************************************************************************/
U8 cFlash::SendByte(U8 byte)
{
#if defined (__FW_BUILD__)
    /*!< Loop while DR register in not emplty */
    while (SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_TXE) == RESET);

    /*!< Send byte through the SPI peripheral */
    SPI_I2S_SendData(sFLASH_SPI, byte);

    /*!< Wait to receive a byte */
    while (SPI_I2S_GetFlagStatus(sFLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET);

    /*!< Return the byte read from the SPI bus */
    return SPI_I2S_ReceiveData(sFLASH_SPI);
#else
    return (0);
#endif
}

/*******************************************************************************
Function name: FlashReadStatus
Description: Read status of flash
Input: byte -> data output
Output: return data received
*******************************************************************************/
U8 cFlash::ReadStatus()
{
    CsLow();
    
    // Send RDSR - Read Status Register opcode
    SendByte(READ_STATUS);
    // Get register contents
    U8 temp = SendByte(0);
    
    CsHigh();

    return temp;
}

bool cFlash::IsBusy()
{
    return ( ( 0 < (ReadStatus() & 0x01) )? true: false );
}

bool cFlash::IsWriteDisable()
{
    return ( (0 == (ReadStatus() & 0x02))? true: false );
}

void cFlash::GlobalProtect()
{
    while( true == IsWriteDisable() )
    {
        // Set the Write Enable latch
        CsLow();
        SendByte(WRITE_ENABLE);
        CsHigh();
    }
    
    CsLow();
    // Send erase chip opcode
    SendByte(WRITE_STATUS);
    SendByte(GLOBAL_PROTECT_CODE);
    // Begin the erase
    CsHigh();
    
    // Wait for erase to complete
    while( true == IsBusy() );

    // Set the Write Disable latch
    CsLow();
    SendByte(WRITE_DISABLE);
    CsHigh();
}

void cFlash::GlobalUnprotect(void)
{
    while( true == IsWriteDisable() )
    {
        // Set the Write Enable latch
        CsLow();
        SendByte(WRITE_ENABLE);
        CsHigh();
    }
    
    CsLow();

    // Send erase chip opcode
    SendByte(WRITE_STATUS);
    SendByte(GLOBAL_UNPROTECT_CODE);

    // Begin the erase
    CsHigh();
    
    // Wait for erase to complete
    while( true == IsBusy() );

    // Set the Write Disable latch
    CsLow();
    SendByte(WRITE_DISABLE);
    CsHigh();
}
    
void cFlash::ReadDeviceID(U8 *pBuff)
{
    CsLow();
    SendByte(READ_DEVICE_ID);
    pBuff[0] = SendByte(0);
    pBuff[1] = SendByte(0);
    pBuff[2] = SendByte(0);
    pBuff[3] = SendByte(0);
    CsHigh();
}    

U8 cFlash::ReadByte(U32 addr)
{
    U8 temp;

    CsLow();

    // Send READ opcode
    SendByte(READ_LOW_SPEED);

    // Send address
    SendByte((U8)(addr>>16));
    SendByte((U8)(addr>>8));
    SendByte((U8)(addr));

    //read data
    temp = SendByte(0);

    CsHigh();

    return (temp);
}

bool cFlash::Read(U32 addr, U8 *pBuff, U16 length)
{
    CsLow();

    // Send READ opcode
    SendByte(READ_LOW_SPEED);

    // Send address
    SendByte((U8)(addr>>16));
    SendByte((U8)(addr>>8));
    SendByte((U8)(addr));

    //read data
    while(length--)
    {
        *pBuff++ = SendByte(0);
    };

    CsHigh();

    return (true);
}


bool cFlash::WriteByte(U32 addr, U8 iData)
{
    // Chip write unprotect
    GlobalUnprotect();

    while( true == IsWriteDisable() )
    {
        // Set the Write Enable latch
        CsLow();
        SendByte(WRITE_ENABLE);
        CsHigh();
    }

    // Send WRITE opcode
    CsLow();

    // Send WRITE opcode
    SendByte(WRITE_1_256);

    // Send address
    SendByte((U8)(addr>>16));
    SendByte((U8)(addr>>8));
    SendByte((U8)(addr));


    // Send the byte to write
    SendByte(iData);

    // Begin the write
    CsHigh();

    // Wait for write to complete
    while( true == IsBusy() );

    // Chip write protect
    GlobalProtect();

    // Set the Write Disable latch
    CsLow();
    SendByte(WRITE_DISABLE);
    CsHigh();

    return (true);
}


bool cFlash::Write(U32 addr, U8 *pBuff, U16 length)
{
    U16 i;

    // Chip write unprotect
    GlobalUnprotect();

    while( true == IsWriteDisable() )
    {
        // Set the Write Enable latch
        CsLow();
        SendByte(WRITE_ENABLE);
        CsHigh();
    }
    
    // Send WRITE opcode
    CsLow();

    // Send WRITE opcode
    SendByte(WRITE_1_256);

    // Send address
    SendByte((U8)(addr>>16));
    SendByte((U8)(addr>>8));
    SendByte((U8)(addr));


    for(i = 0; i < length; i++)
    {
        // Send the byte to write
        SendByte(pBuff[i]);
    }

    // Begin the write
    CsHigh();
    
    // Wait for write to complete
    while( true == IsBusy() );

    // Chip write protect
    GlobalProtect();

    // Set the Write Disable latch
    CsLow();
    SendByte(WRITE_DISABLE);
    CsHigh();
    
    return (true);
}


bool cFlash::EraseBlock(U8 cmd, U32 addr)
{
    // Chip write unprotect
    GlobalUnprotect();

    while( true == IsWriteDisable() )
    {
        // Set the Write Enable latch
        CsLow();
        SendByte(WRITE_ENABLE);
        CsHigh();
    }
    
    CsLow();

    // Send erase opcode
    SendByte(cmd);

    // Send address
    SendByte((U8)(addr>>16));
    SendByte((U8)(addr>>8));
    SendByte((U8)(addr));

    // Begin the erase
    CsHigh();
    
    // Wait for erase to complete
    while( true == IsBusy() );

    // Chip write protect
    GlobalProtect();

    // Set the Write Disable latch
    CsLow();
    SendByte(WRITE_DISABLE);
    CsHigh();
    
    return (true);
}


bool cFlash::EraseChip(void)
{
    // Chip write unprotect
    GlobalUnprotect();

    while( true == IsWriteDisable() )
    {
        // Set the Write Enable latch
        CsLow();
        SendByte(WRITE_ENABLE);
        CsHigh();
    }
    
    CsLow();

    // Send erase chip opcode
    SendByte(CHIP_ERASE1);
    SendByte(CHIP_ERASE2);

    // Begin the erase
    CsHigh();
    
    // Wait for erase to complete
    while( true == IsBusy() );

    // Chip write protect
    GlobalProtect();
    
    // Set the Write Disable latch
    CsLow();
    SendByte(WRITE_DISABLE);
    CsHigh();
    
    return (true);
}

// end of file

